
use std::mem::transmute;
use tcc::*;

#[test]
fn test() {
    let mut tcc = tcc::Tcc::new();
    tcc.set_error_func(Some(Box::new(|err| println!("Error: {}", err))));
    // tcc.add_library("user32.dll").unwrap();

    // Then set the output type, it is possible to create executable files or libraries, but here
    // we want to demonstrate a JIT instead, so letâ€™s build in memory.
    tcc.set_output_type(tcc::OutputType::Memory).unwrap();

    // Now we define a function, it takes one string argument and prints hello on stdout.
    tcc.compile_string(r#"
        int printf(const char *format, ...);
        void print_123() { printf("123\n"); }
        void *LoadLibraryA(const char *lpLibFileName);
        void *GetProcAddress(void *, const char*);
        typedef void (*FnMessageBoxA)(int, char*, char*, int);
        int add(int a, int b) {
            char buf[1000];
            void *h = LoadLibraryA("user32.dll");
            FnMessageBoxA msg = (FnMessageBoxA)GetProcAddress(h, "MessageBoxA");
            sprintf(buf, "%p", msg);
            printf("MessageBoxA: %s", buf);
            msg(0, "abc", "def", 0);
            print_123(); return a + b;
        }
    "#).unwrap();

    // Relocating is required since we want to obtain this say_hello() function afterwards.
    match tcc.relocate() {
        // And here we retrieve it by its symbol name.
        Ok(mut tcc) => {
            println!("All Symbols:");
            tcc.list_symbols(|name, address| {
                println!("  {}: {:x}", name, address);
            });
            if let Some(add) = tcc.get_symbol("add") {
                unsafe {
                    let add: extern "C" fn(i32, i32) -> i32 = transmute(add);
                    println!("add 1 2 : {}", add(1, 2));
                }
            }
        }
        // This failed.
        Err(tcc) => panic!("Relocation failed for {:?}", tcc)
    }
}